<!DOCTYPE html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/core/test_utils.html">
<link rel="import" href="/tracing/extras/chrome/chrome_test_utils.html">
<link rel="import" href="/tracing/extras/importer/trace_event_importer.html">
<link rel="import" href="/tracing/metrics/blink/gc_metric.html">
<link rel="import" href="/tracing/metrics/v8/utils.html">
<link rel="import" href="/tracing/model/slice_group.html">
<link rel="import" href="/tracing/value/histogram_set.html">

<script>
'use strict';

tr.b.unittest.testSuite(function() {
  function createModel(start, end, slices) {
    return tr.e.chrome.ChromeTestUtils.newChromeModel(function(model) {
      const rendererProcess = model.rendererProcess;
      const mainThread = model.rendererMain;
      const group = mainThread.sliceGroup;
      for (const slice of slices) {
        group.pushSlice(tr.c.TestUtils.newSliceEx(slice));
      }
      group.createSubSlices();
      mainThread.updateBounds();
    });
  }

  function constructName(name, suffix) {
    return name + '_' + suffix;
  }

  function run(slices) {
    const histograms = new tr.v.HistogramSet();
    const startTime = slices.reduce(
        (acc, slice) => (Math.min(acc, slice.start)));
    const endTime = slices.reduce((acc, slice) => (Math.max(acc, slice.end)));
    const model = createModel(startTime - 1, endTime + 1, slices);
    tr.metrics.blink.blinkGcMetric(histograms, model);
    return histograms;
  }

  test('topEvents', function() {
    const events = {
      'BlinkGC.AtomicPauseMarkEpilogue': 'blink-gc-atomic-pause-mark-epilogue',
      'BlinkGC.AtomicPauseMarkPrologue': 'blink-gc-atomic-pause-mark-prologue',
      'BlinkGC.AtomicPauseMarkRoots': 'blink-gc-atomic-pause-mark-roots',
      'BlinkGC.AtomicPauseMarkTransitiveClosure':
          'blink-gc-atomic-pause-mark-transitive-closure',
      'BlinkGC.AtomicPauseSweepAndCompact':
          'blink-gc-atomic-pause-sweep-and-compact',
      'BlinkGC.IncrementalMarkingStartMarking': 'blink-gc-incremental-start',
      'BlinkGC.IncrementalMarkingStep': 'blink-gc-incremental-step',
      'BlinkGC.CompleteSweep': 'blink-gc-complete-sweep',
      'BlinkGC.LazySweepInIdle': 'blink-gc-sweep-task-foreground',
      'BlinkGC.LazySweepOnAllocation': 'blink-gc-sweep-allocation',
      'BlinkGC.UnifiedMarkingStep': 'blink-gc-unified-marking-by-v8',
      'BlinkGC.VisitRoots': 'blink-gc-mark-roots'
    };
    for (const [timelineName, telemetryName] of Object.entries(events)) {
      const slices = [
        {
          title: timelineName, args: {}, start: 100, end: 200,
          cpuStart: 100, cpuEnd: 200
        }
      ];
      const actual = run(slices);

      const value = actual.getHistogramNamed(telemetryName);
      assert.strictEqual(value.running.sum, 100);
      assert.strictEqual(value.numValues, 1);
      assert.strictEqual(value.average, 100);
      assert.strictEqual(value.running.max, 100);
      assert.closeTo(value.getApproximatePercentile(0.90), 100, 1);
    }
  });

  test('topEventsPerEpoch', function() {
    const events = {
      'BlinkGC.AtomicPauseMarkEpilogue':
        'blink:gc:main_thread:cycle:full:atomic:mark:epilogue',
      'BlinkGC.AtomicPauseMarkPrologue':
          'blink:gc:main_thread:cycle:full:atomic:mark:prologue',
      'BlinkGC.AtomicPauseMarkRoots':
          'blink:gc:main_thread:cycle:full:atomic:mark:roots',
      'BlinkGC.IncrementalMarkingStartMarking':
          'blink:gc:main_thread:cycle:full:incremental:mark:start',
      'BlinkGC.IncrementalMarkingStep':
          'blink:gc:main_thread:cycle:full:incremental:mark:step',
      'BlinkGC.UnifiedMarkingStep':
          'unified:gc:main_thread:cycle:full:mark:step',
      'BlinkGC.CompleteSweep':
          'blink:gc:main_thread:cycle:full:sweep:complete',
      'BlinkGC.LazySweepInIdle':
          'blink:gc:main_thread:cycle:full:sweep:idle',
      'BlinkGC.LazySweepOnAllocation':
          'blink:gc:main_thread:cycle:full:sweep:on_allocation',
      'BlinkGC.AtomicPauseSweepAndCompact':
          'blink:gc:main_thread:cycle:full:atomic:sweep:compact',
      'BlinkGC.AtomicPauseMarkTransitiveClosure':
            'blink:gc:main_thread:cycle:full:atomic:mark:transitive_closure',
      'BlinkGC.VisitRoots':
          'blink:gc:main_thread:cycle:full:mark:roots'
    };
    for (const [timelineName, telemetryName] of Object.entries(events)) {
      const slices = [
        {
          title: timelineName, args: {'epoch': 0}, start: 100, end: 150,
          cpuStart: 100, cpuEnd: 150
        },
        {
          title: timelineName, args: {'epoch': 0}, start: 150, end: 200,
          cpuStart: 150, cpuEnd: 200
        },
        {
          title: timelineName, args: {'epoch': 1}, start: 200, end: 300,
          cpuStart: 200, cpuEnd: 300
        }
      ];
      const actual = run(slices);

      const value = actual.getHistogramNamed(telemetryName);
      assert.strictEqual(value.running.sum, 200);
      assert.strictEqual(value.numValues, 2);
      assert.strictEqual(value.average, 100);
      assert.strictEqual(value.running.max, 100);
      assert.closeTo(value.getApproximatePercentile(0.90), 100, 1);
    }
  });

  test('totalTimeForBlinkGC', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {'epoch': 0},
        start: 100, end: 110, cpuStart: 100, cpuEnd: 110
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {'epoch': 0},
        start: 110, end: 120, cpuStart: 110, cpuEnd: 120
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 0},
        start: 120, end: 130, cpuStart: 120, cpuEnd: 130
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {'epoch': 0},
        start: 130, end: 140, cpuStart: 130, cpuEnd: 140
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {'epoch': 0},
        start: 140, end: 200, cpuStart: 140, cpuEnd: 200
      },
      {
        title: 'BlinkGC.LazySweepInIdle', args: {'epoch': 0}, start: 210,
        end: 290, cpuStart: 210, cpuEnd: 290
      },
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {'epoch': 1},
        start: 500, end: 550, cpuStart: 500, cpuEnd: 550
      },
      {
        title: 'BlinkGC.IncrementalMarkingStep', args: {'epoch': 1}, start: 550,
        end: 600, cpuStart: 550, cpuEnd: 600
      },
      {
        title: 'BlinkGC.UnifiedMarkingStep', args: {'epoch': 1}, start: 600,
        end: 650, cpuStart: 600, cpuEnd: 650
      },
      // Background events shouldn't contribute to total time.
      {
        title: 'BlinkGC.ConcurrentSweepingStep', args: {'epoch': 1}, start: 300,
        end: 390, cpuStart: 300, cpuEnd: 390
      },
      {
        title: 'BlinkGC.ConcurrentMarkingStep', args: {'epoch': 1}, start: 400,
        end: 450, cpuStart: 400, cpuEnd: 450
      },
      // Explicit roots marking should also not contribute to total time
      // (this is already encompassed by BlinkGC.AtomicPauseMarkRoots and
      // BlinkGC.IncrementalMarkingStartMarking).
      {
        title: 'BlinkGC.VisitRoots', args: {'epoch': 1}, start: 450,
        end: 500, cpuStart: 450, cpuEnd: 500
      }
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed('blink-gc-total');
    assert.strictEqual(value.running.sum, 330);
    assert.strictEqual(value.numValues, 9);
    assert.strictEqual(value.running.max, 80);

    const valuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full');
    assert.strictEqual(valuePerEpoch.running.sum, 330);
    assert.strictEqual(valuePerEpoch.numValues, 2);
    assert.strictEqual(valuePerEpoch.running.max, 180);
  });

  test('totalForegroundSweepingTimeForBlinkGC', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {'epoch': 0},
        start: 100, end: 110, cpuStart: 100, cpuEnd: 110
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {'epoch': 0},
        start: 110, end: 120, cpuStart: 110, cpuEnd: 120
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 0},
        start: 120, end: 130, cpuStart: 120, cpuEnd: 130
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {'epoch': 0},
        start: 130, end: 140, cpuStart: 130, cpuEnd: 140
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {'epoch': 0},
        start: 140, end: 200, cpuStart: 140, cpuEnd: 200
      },
      {
        title: 'BlinkGC.LazySweepInIdle', args: {'epoch': 0},
        start: 210, end: 300, cpuStart: 210, cpuEnd: 300
      },
      {
        title: 'BlinkGC.LazySweepOnAllocation', args: {'epoch': 0},
        start: 310, end: 400, cpuStart: 310, cpuEnd: 400
      },
      {
        title: 'BlinkGC.CompleteSweep', args: {'epoch': 1},
        start: 410, end: 500, cpuStart: 410, cpuEnd: 500
      }
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed('blink-gc-sweep-foreground');
    assert.strictEqual(value.running.sum, 270);
    assert.strictEqual(value.numValues, 3);
    assert.strictEqual(value.average, 90);
    assert.strictEqual(value.running.max, 90);

    const valuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full:sweep');
    assert.strictEqual(valuePerEpoch.running.sum, 270);
    assert.strictEqual(valuePerEpoch.numValues, 2);
    assert.strictEqual(valuePerEpoch.average, 135);
    assert.strictEqual(valuePerEpoch.running.max, 180);
  });

  test('totalBackgroundSweepingTimeForBlinkGC', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.ConcurrentSweepingStep', args: {'epoch': 0},
        start: 100, end: 200, cpuStart: 100, cpuEnd: 200
      },
      {
        title: 'BlinkGC.LazySweepInIdle', args: {'epoch': 0},
        start: 210, end: 300, cpuStart: 210, cpuEnd: 300
      },
      {
        title: 'BlinkGC.ConcurrentSweepingStep', args: {'epoch': 1},
        start: 250, end: 300, cpuStart: 250, cpuEnd: 300
      },
      {
        title: 'BlinkGC.CompleteSweep', args: {'epoch': 1},
        start: 300, end: 400, cpuStart: 310, cpuEnd: 400
      }
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed('blink-gc-sweep-background');
    assert.strictEqual(value.running.sum, 150);
    assert.strictEqual(value.numValues, 2);
    assert.strictEqual(value.average, 75);
    assert.strictEqual(value.running.max, 100);

    const valuePerEpoch = actual.getHistogramNamed(
        'blink:gc:concurrent_thread:cycle:full:sweep');
    assert.strictEqual(valuePerEpoch.running.sum, 150);
    assert.strictEqual(valuePerEpoch.numValues, 2);
    assert.strictEqual(valuePerEpoch.average, 75);
    assert.strictEqual(valuePerEpoch.running.max, 100);
  });

  test('totalMarkingTimeForBlinkGC', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {'epoch': 0},
        start: 0, end: 10, cpuStart: 0, cpuEnd: 10
      },
      {
        title: 'BlinkGC.VisitRoots', args: {'epoch': 0}, start: 10,
        end: 20, cpuStart: 10, cpuEnd: 20
      },
      {
        title: 'BlinkGC.ConcurrentMarkingStep', args: {'epoch': 0},
        start: 20, end: 30, cpuStart: 20, cpuEnd: 30
      },
      {
        title: 'BlinkGC.IncrementalMarkingStep', args: {'epoch': 0},
        start: 30, end: 40, cpuStart: 30, cpuEnd: 40
      },
      {
        title: 'BlinkGC.ConcurrentMarkingStep', args: {'epoch': 1},
        start: 40, end: 44, cpuStart: 40, cpuEnd: 44
      },
      {
        title: 'BlinkGC.UnifiedMarkingStep', args: {'epoch': 0},
        start: 50, end: 60, cpuStart: 50, cpuEnd: 60
      },
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {'epoch': 1},
        start: 60, end: 66, cpuStart: 60, cpuEnd: 66
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {'epoch': 0},
        start: 70, end: 80, cpuStart: 70, cpuEnd: 80
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 1},
        start: 80, end: 87, cpuStart: 80, cpuEnd: 87
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {'epoch': 2},
        start: 90, end: 100, cpuStart: 90, cpuEnd: 100
      }
    ];
    const actual = run(slices);

    const foregroundValue = actual.getHistogramNamed(
        'blink-gc-mark-foreground');
    assert.strictEqual(foregroundValue.running.sum, 63);
    assert.strictEqual(foregroundValue.numValues, 7);
    assert.closeTo(foregroundValue.average, 9, 1e-6);
    assert.strictEqual(foregroundValue.running.max, 10);

    const backgroundValue = actual.getHistogramNamed(
        'blink-gc-mark-background');
    assert.strictEqual(backgroundValue.running.sum, 14);
    assert.strictEqual(backgroundValue.numValues, 2);
    assert.closeTo(backgroundValue.average, 7, 1e-6);
    assert.strictEqual(backgroundValue.running.max, 10);

    const rootsValue = actual.getHistogramNamed('blink-gc-mark-roots');
    assert.strictEqual(rootsValue.running.sum, 10);
    assert.strictEqual(rootsValue.numValues, 1);
    assert.closeTo(rootsValue.average, 10, 1e-6);
    assert.strictEqual(rootsValue.running.max, 10);

    const closueValue = actual.getHistogramNamed(
        'blink-gc-mark-transitive-closure');
    assert.strictEqual(closueValue.running.sum, 27);
    assert.strictEqual(closueValue.numValues, 3);
    assert.closeTo(closueValue.average, 9, 1e-6);
    assert.strictEqual(closueValue.running.max, 10);

    const foregroundValuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full:mark');
    assert.strictEqual(foregroundValuePerEpoch.running.sum, 63);
    assert.strictEqual(foregroundValuePerEpoch.numValues, 3);
    assert.closeTo(foregroundValuePerEpoch.average, 21, 1e-6);
    assert.strictEqual(foregroundValuePerEpoch.running.max, 40);

    const backgroundValuePerEpoch = actual.getHistogramNamed(
        'blink:gc:concurrent_thread:cycle:full:mark');
    assert.strictEqual(backgroundValuePerEpoch.running.sum, 14);
    assert.strictEqual(backgroundValuePerEpoch.numValues, 2);
    assert.closeTo(backgroundValuePerEpoch.average, 7, 1e-6);
    assert.strictEqual(backgroundValuePerEpoch.running.max, 10);

    const rootsValuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full:mark:roots');
    assert.strictEqual(rootsValuePerEpoch.running.sum, 10);
    assert.strictEqual(rootsValuePerEpoch.numValues, 1);
    assert.closeTo(rootsValuePerEpoch.average, 10, 1e-6);
    assert.strictEqual(rootsValuePerEpoch.running.max, 10);

    const closueValuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full:mark:transitive_closure');
    assert.strictEqual(closueValuePerEpoch.running.sum, 27);
    assert.strictEqual(closueValuePerEpoch.numValues, 2);
    assert.closeTo(closueValuePerEpoch.average, 13.5, 1e-6);
    assert.strictEqual(closueValuePerEpoch.running.max, 20);
  });

  test('totalTimeForUnifiedGC', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {},
        start: 100, end: 110, cpuStart: 100, cpuEnd: 110
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {},
        start: 110, end: 120, cpuStart: 110, cpuEnd: 120
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {},
        start: 120, end: 130, cpuStart: 120, cpuEnd: 130
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {},
        start: 130, end: 140, cpuStart: 130, cpuEnd: 140
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {},
        start: 140, end: 200, cpuStart: 140, cpuEnd: 200
      },
      {
        title: 'V8.GCFinalizeMC', args: {},
        start: 200, end: 300, cpuStart: 200, cpuEnd: 300
      },
      // Background events shouldn't contribute to total time.
      {
        title: 'BlinkGC.ConcurrentSweepingStep', args: {},
        start: 420, end: 520, cpuStart: 420, cpuEnd: 520
      },
      {
        title: 'BlinkGC.ConcurrentMarkingStep', args: {},
        start: 520, end: 620, cpuStart: 520, cpuEnd: 620
      }
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed('unified-gc-total');
    assert.strictEqual(value.running.sum, 200);
    assert.strictEqual(value.numValues, 6);
    assert.strictEqual(value.running.max, 100);
  });

  test('totalTimeForUnifiedGCBlinkNestedInV8', function() {
    // Nested Blink GC in V8 top-level GC can happen during unified garbage
    // collection, or when callbacks that trigger e.g. sweeping are fired
    // from V8's GC. These should only be accounted once.
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'V8.GCFinalizeMC', args: {},
        start: 100, end: 300, cpuStart: 100, cpuEnd: 300
      },
      // Nested events should be ignored.
      {
        title: 'BlinkGC.CompleteSweep', args: {},
        start: 200, end: 270, cpuStart: 200, cpuEnd: 270
      },
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {},
        start: 280, end: 290, cpuStart: 280, cpuEnd: 290
      },
      // Next event is outside of nesting and should be accounted for.
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {},
        start: 310, end: 320, cpuStart: 310, cpuEnd: 320
      },
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed('unified-gc-total');
    assert.strictEqual(value.running.sum, 210);
    assert.strictEqual(value.numValues, 2);
    assert.strictEqual(value.average, 105);
    assert.strictEqual(value.running.max, 200);
  });

  function getSlicesWithForcedV8GCs() {
    return [
      {
        title: tr.metrics.v8.utils.forcedGCEventName(), args: {},
        start: 100, end: 300, cpuStart: 100, cpuEnd: 300
      },
      // Following nested events should be ignored.
      {
        title: 'V8.GCFinalizeMC', args: {},
        start: 100, end: 300, cpuStart: 100, cpuEnd: 300
      },
      {
        title: 'BlinkGC.CompleteSweep', args: {},
        start: 200, end: 270, cpuStart: 200, cpuEnd: 270
      },
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {},
        start: 280, end: 290, cpuStart: 280, cpuEnd: 290
      },
      // Next event happens after the forced GC and should be accounted for.
      {
        title: 'BlinkGC.IncrementalMarkingStartMarking', args: {},
        start: 310, end: 320, cpuStart: 310, cpuEnd: 320
      },
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {},
        start: 320, end: 330, cpuStart: 320, cpuEnd: 330
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {},
        start: 330, end: 340, cpuStart: 330, cpuEnd: 340
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {},
        start: 340, end: 350, cpuStart: 340, cpuEnd: 350
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {},
        start: 350, end: 360, cpuStart: 350, cpuEnd: 360
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {},
        start: 360, end: 370, cpuStart: 360, cpuEnd: 370
      },
    ];
  }

  test('ignoreForcedV8GCEventsForUnifiedMetric', function() {
    // Any events nested in a forced GC should be ignored.
    const histograms = new tr.v.HistogramSet();
    const actual = run(getSlicesWithForcedV8GCs());
    const value = actual.getHistogramNamed('unified-gc-total');
    assert.strictEqual(value.running.sum, 60);
    assert.strictEqual(value.numValues, 6);
    assert.strictEqual(value.running.max, 10);
  });

  test('ignoreForcedV8GCEventsForBlinkMetric', function() {
    // Any events nested in a forced GC should be ignored.
    const histograms = new tr.v.HistogramSet();
    const actual = run(getSlicesWithForcedV8GCs());
    const value = actual.getHistogramNamed('blink-gc-total');
    assert.strictEqual(value.running.sum, 60);
    assert.strictEqual(value.numValues, 6);
    assert.strictEqual(value.running.max, 10);
  });

  function getSlicesWithForcedBlinkGCs() {
    return [
      // Following forced events should be ignored.
      {
        title: 'BlinkGC.CompleteSweep', args: {'forced': true},
        start: 200, end: 270, cpuStart: 200, cpuEnd: 270
      },
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {'forced': true},
        start: 320, end: 330, cpuStart: 320, cpuEnd: 330
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {'forced': true},
        start: 330, end: 340, cpuStart: 330, cpuEnd: 340
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure',
        args: {'forced': true},
        start: 340, end: 350, cpuStart: 340, cpuEnd: 350
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {'forced': true},
        start: 350, end: 360, cpuStart: 350, cpuEnd: 360
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {'forced': true},
        start: 360, end: 370, cpuStart: 360, cpuEnd: 370
      },
      // Next events are not forced and should be accounted for.
      {
        title: 'BlinkGC.AtomicPauseMarkPrologue', args: {},
        start: 410, end: 420, cpuStart: 410, cpuEnd: 420
      },
      {
        title: 'BlinkGC.AtomicPauseMarkRoots', args: {},
        start: 420, end: 430, cpuStart: 420, cpuEnd: 430
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {},
        start: 430, end: 440, cpuStart: 430, cpuEnd: 440
      },
      {
        title: 'BlinkGC.AtomicPauseMarkEpilogue', args: {},
        start: 440, end: 450, cpuStart: 440, cpuEnd: 450
      },
      {
        title: 'BlinkGC.AtomicPauseSweepAndCompact', args: {},
        start: 450, end: 460, cpuStart: 450, cpuEnd: 460
      },
    ];
  }

  test('ignoreForcedBlinkGCEventsForUnifiedMetric', function() {
    // Any forced Blink GC events should be ignored.
    const histograms = new tr.v.HistogramSet();
    const actual = run(getSlicesWithForcedBlinkGCs());
    const value = actual.getHistogramNamed('unified-gc-total');
    assert.strictEqual(value.running.sum, 50);
    assert.strictEqual(value.numValues, 5);
    assert.strictEqual(value.running.max, 10);
  });

  test('ignoreForcedBlinkGCEventsForBlinkMetric', function() {
    // Any forced Blink GC events should be ignored.
    const histograms = new tr.v.HistogramSet();
    const actual = run(getSlicesWithForcedBlinkGCs());
    const value = actual.getHistogramNamed('blink-gc-total');
    assert.strictEqual(value.running.sum, 50);
    assert.strictEqual(value.numValues, 5);
    assert.strictEqual(value.running.max, 10);
  });

  test('testAggregatedAtomicPauseTransitiveColsure', function() {
    const histograms = new tr.v.HistogramSet();
    const slices = [
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 0},
        start: 10, end: 20, cpuStart: 10, cpuEnd: 20
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 0},
        start: 40, end: 60, cpuStart: 40, cpuEnd: 60
      },
      {
        title: 'BlinkGC.AtomicPauseMarkTransitiveClosure', args: {'epoch': 1},
        start: 70, end: 75, cpuStart: 70, cpuEnd: 75
      },
    ];
    const actual = run(slices);

    const value = actual.getHistogramNamed(
        'blink-gc-atomic-pause-mark-transitive-closure');
    assert.strictEqual(value.running.sum, 35);
    assert.strictEqual(value.numValues, 3);

    const valuePerEpoch = actual.getHistogramNamed(
        'blink:gc:main_thread:cycle:full:atomic:mark:transitive_closure');
    assert.strictEqual(valuePerEpoch.running.sum, 35);
    assert.strictEqual(valuePerEpoch.numValues, 2);
  });
});
</script>
